【嵌入式 – GD32开发实战指南(ARM版本)】第2部分 外设篇 – 第2章 温湿度传感器AHT10

1 理论分析

1.1 AHT10介绍

AHT10,新一代温湿度传感器在尺寸与智能方面建立了新的标准:它嵌入了适于回流焊的双列扁平无引脚SMD封装,底面4 x 5mm ,高度1.6mm。传感器输出经过标定的数字信号,标准I2C格式。
AHT10 配有一个全新设计的ASIC专用芯片、一个经过改进的MEMS半导体电容式湿度传感元件和一个标准的片上温度传感元件,其性能已经大大提升甚至超出了前一代传感器的可靠性水平,新一代温湿度传感器,经过改进使其在恶劣环境下的性能更稳定。

1.2 GD32读取 AHT10

AHT10读取流程如下:
1.上电后要等待40ms,读取温湿度值之箭,首先要看状态字的校准使能位Bit[3]是否为1(通过发送0x71可以获取一个字节的状态字),如果不为1,要发送0XEi命令(初始化),此命令参数有两个字节,第一个字节为0x08,第二个字节为0x00。
2.直接发送0xAC命令(触发测量),此命令参数有两个字节,第一个字节为0x33,第二个字节为0x00。
3.等待80ms待测量完成,忙状态Bit[7]为0,然后可以读取六个字节(发0X71即可以读取)。

piQY6wn.png

温度湿度读取时序如下:

piQY2F0.png

注:传感器在采集时需要时间,主机发出测量指令(0xAC)后,延时80毫秒以上再读取转换后的数据并判断返回的状态位是否正常。若状态比特位[Bit7]为0代表数据可正常读取,为1时传感器为忙状态,主机需要等待数据处理完成。

AHT10在睡眠模式下,传感器一直等待主机发送测量命令。CPU此时只执行温湿度测量指令,其余指令无法唤醒传感器进行数据采集。

主机发送采集命令后即可唤醒AHT10进行数据采集,启动采集只需要发送7位从机地址,第八位写0即可。

采集数据时,传感器内部的数字信号处理器会对采集到的温湿度数据进行计算,并且内部算法对数据进行校正运算:采集结束后,传感器的输出寄存器会将数据进行更新,

测量周期由湿度和温度转换后的数字信号处理器(DSP)校正计算。在测量周期结束时,数字电源关闭之前输出寄存器将被更新。测量数据以14位的输出,数据位以右对齐。

AHT10输出的温湿度数据为6个字节,真实的相对湿度(%)和温度(℃)数据通过以下公式进行计算。

相对湿度:

piQYRYV.png

温度转换:

piQYWWT.png

2 实验详解

2.1 实验目的

1) 通过实验掌握GD32 芯片GPIO 的配置方法
2) 掌握温湿度传感器AHT10的原理与使用

2.2 实验设备

硬件:PC 机一台;GD32开发板一套; AHT10模块一个
软件:Windows 10系统,Keil5集成开发环境、串口助手

2.3 实验相关电路图

AHT10模块相关电路如下图所示:

piQY4lF.md.png

2.4 AHT10数据读取

AHT10 采用标准的 I2C协议进行通讯。
1.启动传感器
将传感器上电,电压为所选择的VDD电源电压(范围介于1.8V与3.6V之间)。上电后,传感器最多需要 20毫秒时间(此时 SCL为高电平)以达到空闲状态,即做好准备接收由主机(MCU)发送的命令。

2.启动/停止时序
每个传输序列都以 Start状态作为开始并以Stop状态作为结束,如下图所示。

piQY5y4.md.png

启动传输状态(S)-当 SCL 为高电平时,SDA 由高电平转换为低电平。开始状态是由主机控制的一种特殊的总线状态,指示从机传输开始( Start 之后,BUS 总线一般被认为处于占线状态)。

piQYIOJ.md.png

停止传输状态(P)-当SCL 高电平时,SDA 线上从低电平转换为高电平。停止状态是由主机控制的一种特殊的总线状态,指示从机传输结束(Stop 之后,BUS 总线一般被认为处于闲置状态)。

3.发送命令
在启动传输后,随后传输的I2C 首字节包括7位的I2C 设备地址0x38和一个 SDA 方向位(读 R: ‘1’,写 W: ‘0’)。在第 8个 SCL时钟下降沿之后,通过 拉低 SDA 引脚(ACK位),指示传感器数据接收正常。在发出初始化 命令之后(‘1110’0001’代表初始化,‘1010’1100’代表温湿度测量), MCU 必须等待测量完成。

piQYTm9.md.png

4.软复位
这个命令用于在无需关闭和再次打开电源的情况下,重新启动传感器系统。在接收到这个命令之后,传感器系统开始重新初始化,并恢复默认设置状态,软复位所需时间不

核心代码如下:

/**
  * @brief  延时
    @param  count:要延时的ms数
  * @retval None
  */
void aht10_delay(uint32_t count)
{
    uint32_t i;

    for(i = 0; i < count;i++)
    {
        delay_us(100);
    }
}

/**
  * @brief  判断AHT10是否正常
  * @param  addr: I2C地址
  * @retval 1 表示正常, 0 表示不正常
  */
uint8_t aht10_check_ok(uint8_t addr)
{
    if (i2c_check_device(addr) == 0)
    {
        return 1;
    }
    else
    {
        /* 失败后,切记发送I2C总线停止信号 */
        i2c_stop();        
        return 0;
    }

}

/**
  * @brief  AHT10初始化
  * @param  addr: I2C地址
  * @return 0,初始化成功,其他,失败
  */
uint8_t aht10_init(uint8_t addr)
{
    uint8_t res;
    uint8_t temp[2] = {0, 0};

    /*-----------------------------------------------------------------------------------*/  
    i2c_cfg_gpio();

    temp[0] = 0x08;
    temp[1] = 0x00;
    res = aht10_write_data(addr, AHT_CALIBRATION_CMD, temp, 2u);

    if(res != 0)
    {
        return 1;
    }
    aht10_delay(300);

    return res;
}

/**
  * @brief  向ATH写入数据
  * @param  addr:器件IIC地址cmd: 命令
  *         data: 要写入的数据
  *         len: 写入数据大小
  * @return 0,正常,其他,错误代码
  */
uint8_t aht10_write_data(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len)
{
    uint8_t i;

    i2c_start();

    i2c_send_byte((addr<<1)|0);//发送器件地址+写命令, 从机地址是7位,所以左移一位,末位发送0是写操作命令

    if(i2c_wait_ack())       //等待应答 在i2c_wait_ack()中释放总线给从机,从机将SDA拉低 跳过停止位
    {
        i2c_stop();  //产生一个停止条件
        return 1;
    }

    i2c_send_byte(cmd);       //发送命令 
    i2c_wait_ack();        //等待应答

    for(i = 0; i < len; i++)    
    {
        i2c_send_byte(data[i]);     //发送数据 一个字节 
        i2c_wait_ack();        //等待应答
    }

    i2c_stop();  //产生一个停止条件 
    return 0;
}

/**
  * @brief   读一个字节
  * @param   addr:器件IIC地址cmd: 命令
  * @return  读到的数据
  */
uint8_t aht10_read_one_byte(uint8_t addr)
{
    uint8_t res = 0;

    i2c_start();

    i2c_send_byte((addr << 1) | 0X01); //发送器件地址+读命令

    if(i2c_wait_ack())          //等待应答
    {
        i2c_stop();  //产生一个停止条件 
        return 1;
    }

    res = i2c_read_byte();//发送数据
    i2c_nack();//读数据,发送nACK 

    i2c_stop();  //产生一个停止条件 
    return res;
}

/**
  * @brief  读数据
  * @param  addr:器件IIC地址cmd: 命令
  *         data: 数据缓存
  *         len: 读数据大小
  * @return 0,正常,其他,错误代码
  */
uint8_t aht10_read_data(uint8_t addr, uint8_t *data, uint8_t len)
{
    uint32_t i = 0;

    i2c_start();

    i2c_send_byte((addr << 1) | 0X01); //发送器件地址+读命令

    if(i2c_wait_ack())          //等待应答
    {
        i2c_stop();  //产生一个停止条件 
        return 1;
    }

    for(i = 0; i < len; i++)
    {
        if(i == (len - 1))
        {
            data[i] = i2c_read_byte();        //读数据,发送nACK
            i2c_nack();
        }
        else
        {
            data[i] = i2c_read_byte();        
            i2c_ack();//读数据,发送ACK
        }
    }

    i2c_stop();  //产生一个停止条件 
    return 0;
}

/**
  * @brief  读取温度数据
  * @param  addr:器件IIC地址cmd: 命令
  * @return 温度数据(单位:摄氏度)
 */
float aht10_read_temperature(uint8_t addr)
{
    uint8_t res = 0;
    uint8_t cmd[2] = {33, 0};
    uint8_t temp[6];
    float cur_temp;

    res = aht10_write_data(addr, AHT_GET_DATA, cmd, 2); //发送读取数据命令
    if(res)
    {
        return 1;
    }

    res = aht10_read_data(addr, temp, 6);                //读取数据
    if(res)
    {
        return 1;
    }

    cur_temp = ((temp[3] & 0xf) << 16 | temp[4] << 8 | temp[5]) * 200.0 / (1 << 20) - 50;

    return cur_temp;
}

/**
  * @brief  读取湿度数据
  * @param  addr:器件IIC地址cmd: 命令
  * @return 湿度数据(单位:%RH)
  */
float aht10_read_humidity(uint8_t addr)
{
    uint8_t res = 0;
    uint8_t cmd[2] = {33, 0};
    uint8_t humi[6];
    float cur_humi;

    res = aht10_write_data(addr, AHT_GET_DATA, cmd, 2); //发送读取数据命令
    if(res)
    {
        return 1;
    }

    res = aht10_read_data(addr, humi, 6);    //读取数据
    if(res)
    {
        return 1;
    }

    cur_humi = ((humi[1]) << 12 | humi[2] << 4 | (humi[3] & 0xF0)) * 100.0 / (1 << 20);

    return cur_humi;
}

主函数代码如下。

/*
    brief      main function
    param[in]  none
    param[out] none
    retval     none
*/
int main(void)
{
    st_aht10_data aht10_data;
    st_bsp_usart_dev bsp_usart_dev0 = USART_DEV0_CONFIG;
    st_bsp_led_dev bsp_led_dev0 = LED_DEV0_CONFIG;

    //systick init
    sysTick_init();

    // LED1 init
    bsp_led_init(&bsp_led_dev0);
    //usart init 115200 8-N-1
    bsp_usart_init(&bsp_usart_dev0, USART_MODE_EXTI, 115200, 0, 1);

    // DMA config
    bsp_usart_dma_init();

    /* USART DMA 发送使能 */
    usart_dma_enable(USART0, USART_DMA_TRANSMIT);
    /* USART DMA接收使能 */
    usart_dma_enable(USART0, USART_DMA_RECEIVE);

    aht10_init(AHT_IIC_ADDR);

    while(1)
    {
        aht10_data.temp = aht10_read_temperature(AHT_IIC_ADDR);
        aht10_data.hum = aht10_read_humidity(AHT_IIC_ADDR);

        printf("temp = %.2f°C, hum = %.2f%%\r\n", aht10_data.temp, aht10_data.hum);
        bsp_led_toggle(&bsp_led_dev0);
        delay_ms(1000);
    }
}

2.5 实验现象

接上AHT10设备后,每隔1s就输出温湿度。

piQa89P.md.png



欢迎访问我的网站

BruceOu的哔哩哔哩
BruceOu的主页
BruceOu的博客
BruceOu的CSDN博客
BruceOu的简书
BruceOu的知乎


欢迎订阅我的微信公众号

关注公众号[嵌入式实验楼]获取更多资讯


欢迎订阅我的知识星球

关注知识星球[嵌入式实验楼]获取更多资讯


资源获取方式

1.关注公众号[嵌入式实验楼]
2.在公众号回复关键词[GD32开发实战指南]获取资料提取码

Related posts

Leave a Comment